﻿//=====================================================

//Copyright (C) 2016-2018 Fanjia

//All rights reserved

//CLR版 本:    4.0.30319.42000

//创建时间:     2018/11/13 18:02:39

//创 建 人:   徐晓航

//======================================================

using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;

namespace JobBatDeal.Common
{
    public static class StringExtension
    {
        /// <summary>
        /// returns a string buffer of characters from string1 within string2 if they are of a given
        /// distance separation from the position in string1.
        /// </summary>
        /// <param name="firstWord">string one</param>
        /// <param name="secondWord">string two</param>
        /// <param name="distanceSep">separation distance</param>
        /// <returns>a string buffer of characters from string1 within string2 if they are of a given
        /// distance separation from the position in string1</returns>
        private static StringBuilder GetCommonCharacters(string firstWord, string secondWord, int distanceSep)
        {
            var returnCommons = new StringBuilder();
            var copy = new StringBuilder(secondWord);
            for (var i = 0; i < firstWord.Length; i++)
            {
                var ch = firstWord[i];
                var foundIt = false;
                for (var j = Math.Max(0, i - distanceSep);
                    !foundIt && j < Math.Min(i + distanceSep, secondWord.Length);
                    j++)
                {
                    if (copy[j] != ch) continue;
                    foundIt = true;
                    returnCommons.Append(ch);
                    copy[j] = '#';
                }
            }
            return returnCommons;
        }

        /// <summary>
        /// gets the similarity of the two strings using Jaro distance.
        /// </summary>
        /// <param name="firstWord"></param>
        /// <param name="secondWord"></param>
        /// <returns>a value between 0-1 of the similarity</returns>
        private static double GetJaroSimilarity(string firstWord, string secondWord)
        {
            //get half the length of the string rounded up - (this is the distance used for acceptable transpositions)
            var halfpence = Math.Max(firstWord.Length, secondWord.Length) / 2 + 1;
            //get common characters
            var common1 = GetCommonCharacters(firstWord, secondWord, halfpence);
            var commonMatches = common1.Length;
            //check for zero in common
            if (commonMatches == 0)
            {
                return 0;
            }
            var common2 = GetCommonCharacters(secondWord, firstWord, halfpence);
            //check for same length common strings returning 0.0f is not the same
            if (commonMatches != common2.Length)
            {
                return 0;
            }
            //get the number of transpositions
            var transpositions = 0;
            for (var i = 0; i < commonMatches; i++)
            {
                if (common1[i] != common2[i])
                {
                    transpositions++;
                }
            }

            //calculate jaro metric
            transpositions /= 2;
            var jaroSimilarity = commonMatches / (3.0 * firstWord.Length)
                       + commonMatches / (3.0 * secondWord.Length)
                       + (commonMatches - transpositions) / (3.0 * commonMatches);
            return jaroSimilarity;
        }

        /// <summary>
        /// gets the prefix length found of common characters at the begining of the strings.
        /// </summary>
        /// <param name="firstWord"></param>
        /// <param name="secondWord"></param>
        /// <returns>the prefix length found of common characters at the begining of the strings</returns>
        private static int GetPrefixLength(string firstWord, string secondWord)
        {
            var n = Math.Min(Math.Min(firstWord.Length, secondWord.Length), 4);
            for (var i = 0; i < n; i++)
            {
                if (firstWord[i] != secondWord[i])
                {
                    return i;
                }
            }
            return n;
        }

        ///// <summary>
        ///// 获取 JaroWinkler 相似度
        ///// </summary>
        ///// <param name="source">源字符串</param>
        ///// <param name="target">目标字符串</param>
        ///// <returns></returns>
        //public static double GetJaroWinklerSimilarity(this string source, string target)
        //{
        //    if (string.IsNullOrWhiteSpace(source) || string.IsNullOrWhiteSpace(target))
        //    {
        //        return 0;
        //    }

        //    var s = source.ToCleanStr();
        //    var t = target.ToCleanStr();
        //    if (s == t)
        //    {
        //        return 1;
        //    }

        //    var jaro = GetJaroSimilarity(s, t);
        //    var prefixLength = GetPrefixLength(s, t);
        //    return jaro + prefixLength * 0.1 * (1 - jaro);
        //}

        ///// <summary>
        ///// 清理字符串
        ///// </summary>
        ///// <param name="input"></param>
        ///// <returns></returns>
        //public static string ToCleanStr(this string input)
        //{
        //    return string.IsNullOrEmpty(input) ?
        //        string.Empty :
        //        Regex.Replace(input, @"[\s|\·|\—|\-|\(|\)|\【|\】|\（|\）]", "", RegexOptions.IgnoreCase);
        //}

        /// <summary>
        /// 从XML字符串中反序列化对象
        /// </summary>
        /// <typeparam name="T">结果对象类型</typeparam>
        /// <param name="input">包含对象的XML字符串</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>反序列化得到的对象</returns>
        public static T XmlDeserialize<T>(this string input, Encoding encoding = null)
        {
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            if (string.IsNullOrEmpty(input))
                throw new ArgumentNullException(input);
            var mySerializer = new XmlSerializer(typeof(T));
            using (var ms = new MemoryStream(encoding.GetBytes(input)))
            {
                using (var sr = new StreamReader(ms, encoding))
                {
                    return (T)mySerializer.Deserialize(sr);
                }
            }
        }
    }
}
